package cn.wlinker.driver.modbus.tcp.utils;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.text.StrPool;
import cn.wlinker.driver.common.exception.NotSupportException;
import cn.wlinker.driver.common.exception.ReadException;
import cn.wlinker.driver.common.exception.WriteException;
import cn.wlinker.driver.modbus.tcp.constant.DataTypeEnum;
import cn.wlinker.driver.modbus.tcp.constant.FunctionEnum;
import cn.wlinker.driver.modbus.tcp.domain.ModbusBean;
import cn.wlinker.driver.modbus.tcp.domain.ModbusRes;
import cn.wlinker.driver.modbus.tcp.domain.ModbusResItem;
import com.serotonin.modbus4j.exception.ErrorResponseException;
import com.serotonin.modbus4j.exception.ModbusTransportException;

import java.util.LinkedList;
import java.util.List;
import java.util.Optional;

/**
 * modbus读写请求封装
 *
 * @author gxsjx
 * @version 1.0
 * @date 2022/10/10
 * Copyright © wlinker.cn
 */
public class ModbusHelper {

    private static boolean getBooleanByData(String data) {
        return CharSequenceUtil.isNotEmpty(data) && "1".equalsIgnoreCase(data);
    }

    private static void checkParams(String ip, Integer port, ModbusBean modbusBean, Boolean isReading) {
        Assert.notEmpty(ip, "ip地址不能为空!!!");
        Assert.notNull(port, "端口号不能为空!!!");
        Assert.notNull(modbusBean, "参数不能为空!!!");
        Integer slaveId = modbusBean.getSlaveId();
        Assert.notNull(slaveId, "从机地址slaveId不能为空!!!");
        Integer address = modbusBean.getAddress();
        Assert.notNull(address, "起始地址不能为空!!!");
        Integer functionType = modbusBean.getFunctionType();
        Optional<FunctionEnum> functionEnumOptional = FunctionEnum.getByType(functionType);
        if(!functionEnumOptional.isPresent()){
            throw new NotSupportException("不支持的功能码");
        }
        FunctionEnum functionEnum = functionEnumOptional.get();
        if (FunctionEnum.COIL_STATUS.equals(functionEnum) || FunctionEnum.INPUT_STATUS.equals(functionEnum)) {
            modbusBean.setDataType(DataTypeEnum.COIL.getType());
        }
        if (Boolean.TRUE.equals(isReading)) {
            Integer quantity = modbusBean.getQuantity();
            Assert.notNull(quantity, "数据个数不能为空!!!");
            Integer dataType = modbusBean.getDataType();
            Optional<DataTypeEnum> dataTypeEnum = DataTypeEnum.getByType(dataType);
            Assert.isTrue(dataTypeEnum.isPresent(), "数据类型不能为空!!!");
        }
    }


    public static ModbusRes read(String ip, Integer port, ModbusBean modbusBean) throws ModbusTransportException, ErrorResponseException {
        checkParams(ip, port, modbusBean, true);
        Integer slaveId = modbusBean.getSlaveId();
        Integer address = modbusBean.getAddress();
        Integer quantity = modbusBean.getQuantity();
        Integer dataType = modbusBean.getDataType();
        Optional<DataTypeEnum> dataTypeEnum = DataTypeEnum.getByType(dataType);
        //每个数据的寄存器数
        if(!dataTypeEnum.isPresent()){
            throw new NotSupportException("不支持的数据类型");
        }
        int registerCount = dataTypeEnum.get().getRegisterCount();
        List<ModbusResItem> resBeanList;
        Modbus4jReader modbus4jReader = new Modbus4jReader(ip, port);
        Optional<FunctionEnum> functionEnumOptional = FunctionEnum.getByType(modbusBean.getFunctionType());
        if (!functionEnumOptional.isPresent()) {
            throw new NotSupportException("不支持的功能码");
        }
        switch (functionEnumOptional.get()) {
            case COIL_STATUS:
                boolean[] booleans = modbus4jReader.readCoilStatus(slaveId, address, quantity * registerCount);
                resBeanList = ModbusResultUtils.getItemList(modbusBean, booleans);
                break;
            case INPUT_STATUS:
                booleans = modbus4jReader.readInputStatus(slaveId, address, quantity * registerCount);
                resBeanList = ModbusResultUtils.getItemList(modbusBean, booleans);
                break;
            case HOLDING_REGISTER:
                List<Number> numberList = new LinkedList<>();
                for (int i = 0; i < quantity; i++) {
                    int currentAddress = address + i * registerCount;
                    Number number = modbus4jReader.readHoldingRegisterByDataType(slaveId, currentAddress, dataType);
                    numberList.add(number);
                }
                resBeanList = ModbusResultUtils.getItemList(modbusBean, numberList, registerCount);
                break;
            case INPUT_REGISTER:
                numberList = new LinkedList<>();
                for (int i = 0; i < quantity; i++) {
                    int currentAddress = address + i * registerCount;
                    Number number = modbus4jReader.readInputRegisterByDataType(slaveId, currentAddress, dataType);
                    numberList.add(number);
                }
                resBeanList = ModbusResultUtils.getItemList(modbusBean, numberList, registerCount);
                break;
            default:
                throw new ReadException("功能码不合法");
        }
        return new ModbusRes(ip, port, resBeanList);
    }

    public static Boolean write(String ip, Integer port, ModbusBean modbusBean) throws ModbusTransportException {
        checkParams(ip, port, modbusBean, false);
        String value = modbusBean.getValue();
        if (CharSequenceUtil.isEmpty(value)) {
            throw new WriteException( "数据值不能为空!!!");
        }
        Integer slaveId = modbusBean.getSlaveId();
        Optional<FunctionEnum> functionEnumOptional = FunctionEnum.getByType(modbusBean.getFunctionType());
        Integer address = modbusBean.getAddress();
        Modbus4jWriter modbus4jWriter = new Modbus4jWriter(ip, port);
        boolean result;
        if(!functionEnumOptional.isPresent()){
            throw new WriteException("功能码不合法");
        }
        switch (functionEnumOptional.get()) {
            case COIL_STATUS:
                if (!value.contains(StrPool.COMMA)) {
                    result = modbus4jWriter.writeCoil(slaveId, address, getBooleanByData(value));
                } else {
                    String[] valueArr = value.split(StrPool.COMMA);
                    int valueCount = valueArr.length;
                    boolean[] booleanValues = new boolean[valueCount];
                    for (int i = 0; i < valueArr.length; i++) {
                        booleanValues[i] = getBooleanByData(valueArr[i]);
                    }
                    result = modbus4jWriter.writeCoils(slaveId, address, booleanValues);
                }
                break;
            case HOLDING_REGISTER:
                if (!value.contains(StrPool.COMMA)) {
                    result = modbus4jWriter.writeHoldingRegister(slaveId, address, Short.parseShort(value));
                } else {
                    String[] valueArr = value.split(StrPool.COMMA);
                    int valueCount = valueArr.length;
                    short[] shortValues = new short[valueCount];
                    for (int i = 0; i < valueArr.length; i++) {
                        shortValues[i] = Short.parseShort(valueArr[i]);
                    }
                    result = modbus4jWriter.writeHoldingRegisters(slaveId, address, shortValues);
                }
                break;
            default:
                throw new WriteException("功能码不合法");
        }
        return result;
    }
}


